<?php

namespace yii\behaviors;

use yii\base\InvalidCallException;
use yii\db\BaseActiveRecord;

/**
 * TimestampBehavior 自动使用当前时间戳填充指定的属性。
 *
 * 要使用 TimestampBehavior，请将以下代码插入ActiveRecord类：
 *
 * ```php
 * use yii\behaviors\TimestampBehavior;
 *
 * public function behaviors()
 * {
 *     return [
 *         TimestampBehavior::className(),
 *     ];
 * }
 * ```
 *
 * 默认情况下，TimestampBehavior 将在插入关联的AR对象时使用当前时间戳填充 `created_at` 和 `updated_at` 属性;
 * 当更新AR对象时，它将使用时间戳填充`updated_at`属性。 时间戳值由 `time()` 获得。
 * 
 * 因为属性值将由此行为自动设置，所以它们通常不是用户输入，因此不应进行验证，
 * 即`[created_at`和`updated_at`不应出现在[[\ yii \ base \ Model :: rules()中 | rules()]]。
 *
 * 要使上述实现与MySQL数据库一起使用，请将列（`created_at`，`updated_at`）声明为int（11）作为UNIX时间戳。
 *
 * 如果属性名称不同或者您想使用不同的计算时间戳的方法，则可以配置[[createdAtAttribute]]，
 * [[updatedAtAttribute]]和[[value]]属性，如下所示：
 *
 * ```php
 * use yii\db\Expression;
 *
 * public function behaviors()
 * {
 *     return [
 *         [
 *             'class' => TimestampBehavior::className(),
 *             'createdAtAttribute' => 'create_time',
 *             'updatedAtAttribute' => 'update_time',
 *             'value' => new Expression('NOW()'),
 *         ],
 *     ];
 * }
 * ```
 *
 * 如果您使用上面示例中的[[\ yii \ db \ Expression]]对象，则该属性将不保存时间戳值，
 * 但保存记录后的Expression对象本身。 如果之后需要DB中的值，
 * 则应调用记录的[[\ yii \ db \ ActiveRecord :: refresh()| refresh()]]方法。
 *
 * TimestampBehavior 还提供名为[[touch()]]的方法，
 * 该方法允许您将当前时间戳分配给指定的属性并将其保存到数据库中。 例如，
 *
 * ```php
 * $model->touch('creation_time');
 * ```
 */
class TimestampBehavior extends AttributeBehavior
{
    /**
     * @var string the attribute that will receive timestamp value
     * Set this property to false if you do not want to record the creation time.
     */
    public $createdAtAttribute = 'created_at';
    /**
     * @var string the attribute that will receive timestamp value.
     * Set this property to false if you do not want to record the update time.
     */
    public $updatedAtAttribute = 'updated_at';
    /**
     * {@inheritdoc}
     *
     * In case, when the value is `null`, the result of the PHP function [time()](http://php.net/manual/en/function.time.php)
     * will be used as value.
     */
    public $value;


    /**
     * {@inheritdoc}
     */
    public function init()
    {
        parent::init();

        if (empty($this->attributes)) {
            $this->attributes = [
                BaseActiveRecord::EVENT_BEFORE_INSERT => [$this->createdAtAttribute, $this->updatedAtAttribute],
                BaseActiveRecord::EVENT_BEFORE_UPDATE => $this->updatedAtAttribute,
            ];
        }
    }

    /**
     * {@inheritdoc}
     *
     * In case, when the [[value]] is `null`, the result of the PHP function [time()](http://php.net/manual/en/function.time.php)
     * will be used as value.
     */
    protected function getValue($event)
    {
        if ($this->value === null) {
            return time();
        }

        return parent::getValue($event);
    }

    /**
     * Updates a timestamp attribute to the current timestamp.
     *
     * ```php
     * $model->touch('lastVisit');
     * ```
     * @param string $attribute the name of the attribute to update.
     * @throws InvalidCallException if owner is a new record (since version 2.0.6).
     */
    public function touch($attribute)
    {
        /* @var $owner BaseActiveRecord */
        $owner = $this->owner;
        if ($owner->getIsNewRecord()) {
            throw new InvalidCallException('Updating the timestamp is not possible on a new record.');
        }
        $owner->updateAttributes(array_fill_keys((array) $attribute, $this->getValue(null)));
    }
}
